/**
 * 
 */
package com.aqarat.controller;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpSession;

import org.primefaces.model.SortOrder;

import com.aqarat.entity.Apartment;
import com.aqarat.entity.Building;
import com.aqarat.entity.Customer;
import com.aqarat.entity.Expens;
import com.aqarat.entity.Floor;
import com.aqarat.entity.Payment;
import com.aqarat.entity.resultsetmapping.PaymentResultSet;
import com.core.entity.Lookup;
import com.core.transaction.EntityManagerClass;
import com.core.util.Utils;
import com.entity.RegisteredUser;

/**
 * @author karimsherif
 * 
 */
public class AqaratControllerImpl implements AqaratController, Serializable {
	private static final long serialVersionUID = 1L;
	RegisteredUser registeredUser;
	HttpSession session;
	EntityManager em = null;

	public AqaratControllerImpl() {
		em = EntityManagerClass.getEntityManagerFactory().createEntityManager();
	}

	public Building getBuildingById(Long buildingId) {
		return (Building) em.createQuery("select b from Building b where b.recid=:buildingId").setParameter("buildingId", buildingId).getSingleResult();
	}

	public void saveBuilding(Building building, Floor floor, List<Apartment> apartmentList) throws Exception {
		try {
			em.getTransaction().begin();
			building.setExpenses(0.0);
			building.setRevenues(0.0);
			if (building.getRecid() == null) {
				em.persist(building);
			} else {
				em.merge(building);
			}
			for (int i = 1; i <= building.getNumberOfFloors(); i++) {
				Floor flooor = new Floor();
				flooor.setNumberOfApartments(floor.getNumberOfApartments());
				flooor.setNo((long) i);
				flooor.setBuildingId(building.getRecid());
				if (flooor.getRecid() == null) {
					em.persist(flooor);
				} else {
					em.persist(flooor);
				}
				Long apartmentNo = 1L;

				for (Apartment apartmentt : apartmentList) {
					Apartment apartment = new Apartment();
					apartment.setFloorId(flooor.getRecid());
					apartment.setNo(apartmentNo);
					apartment.setArea(apartmentt.getArea());
					apartment.setFirstPayment(0D);
					apartment.setConstructStatus(Lookup.APARTMENT_CONSTRUCTSTATUS_ONAIR.getRecid());
					apartment.setReserveStatus(Lookup.APARTMENT_RESERVATIONSTATUS_NOTRESERVED.getRecid());
					apartment.setTaxesStatus(Lookup.APARTMENT_TAXESSSTATUS_NOTPAYED.getRecid());
					if (apartment.getRecid() == null) {
						em.persist(apartment);
					} else {
						em.persist(apartment);
					}
					apartmentNo++;
				}
			}

			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}

	}

	public void saveApartment(Apartment apartment) throws Exception {
		try {
			em.getTransaction().begin();
			if (apartment.getRecid() == null) {
				em.persist(apartment);
			} else {
				em.merge(apartment);
			}
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}
	}

	public void deleteApartment(Apartment apartment) throws Exception {
		try {
			em.getTransaction().begin();
			em.remove(apartment);
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}

	}

	public List<Building> getBuildingList(Building building) throws Exception {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Building> criteriaQuery = criteriaBuilder.createQuery(Building.class);
		Root<Building> buildingRoot = criteriaQuery.from(Building.class);

		criteriaQuery.select(buildingRoot);

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (building != null) {
			if (building.getRecid() != null) {
				criteriaTemp = criteriaBuilder.equal(buildingRoot.get("recid"), building.getRecid());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (building.getName() != null) {
				criteriaTemp = criteriaBuilder.like((Expression) buildingRoot.get("name"), "%" + building.getName() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (building.getNumberOfFloors() != null) {
				criteriaTemp = criteriaBuilder.equal(buildingRoot.get("numberOfFloors"), building.getNumberOfFloors());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
		}

		if (null != criteria) {
			criteriaQuery.where(criteria);
		}

		Query query = em.createQuery(criteriaQuery);
		return query.getResultList();
		// return em.createQuery("select b from Building b").getResultList();
	}

	public List<Building> getBuildingLookupList() {
		return em.createQuery("select new Building(b.recid,b.name) from Building b ").getResultList();
	}

	public List<Floor> getFloorListByBuildingId(Long buildingId) throws Exception {
		return em.createQuery("select f from Floor f where f.buildingId=:buildingId").setParameter("buildingId", buildingId).getResultList();
	}

	public List<Apartment> getApartmentList_(Apartment apartment) throws Exception {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Apartment> criteriaQuery = criteriaBuilder.createQuery(Apartment.class);
		Root<Apartment> apartmentRoot = criteriaQuery.from(Apartment.class);
		// Join<Floor, Apartment> floorApartmentRoot =
		// apartmentRoot.join("floor",
		// JoinType.LEFT);

		criteriaQuery.select(apartmentRoot);

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isEmpty(apartment.getFloor().getBuilding().getRecid())) {
			if (Utils.isEmpty(apartment.getFloor().getNo())) {
				// no conditions to add
			} else {
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("floor").get("no"), apartment.getFloor().getNo());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

		} else {
			// means that building recid not null
			if (Utils.isEmpty(apartment.getFloor().getNo())) {
				// means that floor no is null >>building recid not null &&
				// floor is null
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				Join<Building, Floor> buildigFloorRoot = floorApartmentRoot.join("building", JoinType.LEFT);
				criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("floor").get("building").get("recid"), apartment.getFloor().getBuilding().getRecid());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			} else {
				// means that floor no is not null >>building recid not null &&
				// floor no is not null
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				Join<Building, Floor> buildigFloorRoot = floorApartmentRoot.join("building", JoinType.LEFT);
				Predicate criteriaTemp1 = criteriaBuilder.equal(apartmentRoot.get("floor").get("no"), apartment.getFloor().getNo());
				Predicate criteriaTemp2 = criteriaBuilder.equal(apartmentRoot.get("floor").get("building").get("recid"), apartment.getFloor().getBuilding().getRecid());
				criteriaTemp = criteriaBuilder.and(criteriaTemp1, criteriaTemp2);
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
		}
		if (Utils.isNotEmpty(apartment.getReserveStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("reserveStatus"), apartment.getReserveStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}
		if (Utils.isNotEmpty(apartment.getConstructStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("constructStatus"), apartment.getConstructStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}

		if (Utils.isNotEmpty(apartment.getTaxesStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("taxesStatus"), apartment.getTaxesStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}

		if (null != criteria) {
			criteriaQuery.where(criteria);
		}

		Query query = em.createQuery(criteriaQuery);
		return query.getResultList();
	}

	public List<Apartment> getApartmentList(int first, int pageSize, Apartment apartment){
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Apartment> criteriaQuery = criteriaBuilder.createQuery(Apartment.class);
		Root<Apartment> apartmentRoot = criteriaQuery.from(Apartment.class);

		criteriaQuery.select(apartmentRoot);

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isEmpty(apartment.getFloor().getBuilding().getRecid())) {
			if (Utils.isEmpty(apartment.getFloor().getNo())) {
				// no conditions to add
			} else {
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("floor").get("no"), apartment.getFloor().getNo());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

		} else {
			// means that building recid not null
			if (Utils.isEmpty(apartment.getFloor().getNo())) {
				// means that floor no is null >>building recid not null &&
				// floor is null
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				Join<Building, Floor> buildigFloorRoot = floorApartmentRoot.join("building", JoinType.LEFT);
				criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("floor").get("building").get("recid"), apartment.getFloor().getBuilding().getRecid());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			} else {
				// means that floor no is not null >>building recid not null &&
				// floor no is not null
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				Join<Building, Floor> buildigFloorRoot = floorApartmentRoot.join("building", JoinType.LEFT);
				Predicate criteriaTemp1 = criteriaBuilder.equal(apartmentRoot.get("floor").get("no"), apartment.getFloor().getNo());
				Predicate criteriaTemp2 = criteriaBuilder.equal(apartmentRoot.get("floor").get("building").get("recid"), apartment.getFloor().getBuilding().getRecid());
				criteriaTemp = criteriaBuilder.and(criteriaTemp1, criteriaTemp2);
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
		}
		if (Utils.isNotEmpty(apartment.getReserveStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("reserveStatus"), apartment.getReserveStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}
		if (Utils.isNotEmpty(apartment.getConstructStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("constructStatus"), apartment.getConstructStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}

		if (Utils.isNotEmpty(apartment.getTaxesStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("taxesStatus"), apartment.getTaxesStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}

		if (null != criteria) {
			criteriaQuery.where(criteria);
		}

		Query query = em.createQuery(criteriaQuery);
		if (first != -1 && pageSize != -1) {
			query.setMaxResults(pageSize);
			query.setFirstResult(first);
		}
		return query.getResultList();
	}

	public Long getApartmentListRowCount(Apartment apartment) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
		Root<Apartment> apartmentRoot = criteriaQuery.from(Apartment.class);

		criteriaQuery.select(criteriaBuilder.count(apartmentRoot.get("recid")));

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isEmpty(apartment.getFloor().getBuilding().getRecid())) {
			if (Utils.isEmpty(apartment.getFloor().getNo())) {
				// no conditions to add
			} else {
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("floor").get("no"), apartment.getFloor().getNo());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

		} else {
			// means that building recid not null
			if (Utils.isEmpty(apartment.getFloor().getNo())) {
				// means that floor no is null >>building recid not null &&
				// floor is null
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				Join<Building, Floor> buildigFloorRoot = floorApartmentRoot.join("building", JoinType.LEFT);
				criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("floor").get("building").get("recid"), apartment.getFloor().getBuilding().getRecid());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			} else {
				// means that floor no is not null >>building recid not null &&
				// floor no is not null
				Join<Floor, Apartment> floorApartmentRoot = apartmentRoot.join("floor", JoinType.LEFT);
				Join<Building, Floor> buildigFloorRoot = floorApartmentRoot.join("building", JoinType.LEFT);
				Predicate criteriaTemp1 = criteriaBuilder.equal(apartmentRoot.get("floor").get("no"), apartment.getFloor().getNo());
				Predicate criteriaTemp2 = criteriaBuilder.equal(apartmentRoot.get("floor").get("building").get("recid"), apartment.getFloor().getBuilding().getRecid());
				criteriaTemp = criteriaBuilder.and(criteriaTemp1, criteriaTemp2);
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
		}
		if (Utils.isNotEmpty(apartment.getReserveStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("reserveStatus"), apartment.getReserveStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}
		if (Utils.isNotEmpty(apartment.getConstructStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("constructStatus"), apartment.getConstructStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}

		if (Utils.isNotEmpty(apartment.getTaxesStatus())) {
			criteriaTemp = criteriaBuilder.equal(apartmentRoot.get("taxesStatus"), apartment.getTaxesStatus());
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
		}

		if (null != criteria) {
			criteriaQuery.where(criteria);
		}

		Query query = em.createQuery(criteriaQuery);
		return Long.parseLong(query.getSingleResult() + "");

	}

	// ============================//
	public Lookup getLookup(Long lookupId) throws Exception {
		return (Lookup) em.createQuery("select l from Lookup l where l.recid=:lookupId").setParameter("lookupId", lookupId).getSingleResult();

	}

	public List<Lookup> getLookupList(Long lookupTypeId) throws Exception {
		return em.createQuery("select l from Lookup l where l.lookupType.recid=:lookupTypeId").setParameter("lookupTypeId", lookupTypeId).getResultList();

	}

	// -----------------------------------------------------------------------------------------//
	public List<Customer> getCustomerList(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters, Customer customer) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
		Root<Customer> customerRoot = criteriaQuery.from(Customer.class);
		criteriaQuery.select(customerRoot);

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isNotEmpty(customer)) {
			if (Utils.isNotEmpty(customer.getName())) {
				criteriaTemp = criteriaBuilder.like((Expression) customerRoot.get("name"), "%" + customer.getName() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (Utils.isNotEmpty(customer.getCardNo())) {
				criteriaTemp = criteriaBuilder.like((Expression) customerRoot.get("cardNo"), "%" + customer.getCardNo() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (Utils.isNotEmpty(customer.getMobile())) {
				criteriaTemp = criteriaBuilder.like((Expression) customerRoot.get("mobile"), "%" + customer.getMobile() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (null != criteria) {
				criteriaQuery.where(criteria);
			}
		}
		Query query = em.createQuery(criteriaQuery);
		if (first != -1 && pageSize != -1) {
			query.setMaxResults(pageSize);
			query.setFirstResult(first);
		}
		return query.getResultList();
	}

	public Long getCustomerListRowCount(Customer customer) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
		Root<Customer> customerRoot = criteriaQuery.from(Customer.class);
		criteriaQuery.select(criteriaBuilder.count(customerRoot.get("recid")));
		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isNotEmpty(customer)) {
			if (Utils.isNotEmpty(customer.getName())) {
				criteriaTemp = criteriaBuilder.like((Expression) customerRoot.get("name"), "%" + customer.getName() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (Utils.isNotEmpty(customer.getCardNo())) {
				criteriaTemp = criteriaBuilder.like((Expression) customerRoot.get("cardNo"), "%" + customer.getCardNo() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (Utils.isNotEmpty(customer.getMobile())) {
				criteriaTemp = criteriaBuilder.like((Expression) customerRoot.get("mobile"), "%" + customer.getMobile() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (null != criteria) {
				criteriaQuery.where(criteria);
			}
		}
		Query query = em.createQuery(criteriaQuery);
		return Long.parseLong(query.getSingleResult() + "");

	}

	// -----------------------------------------------------------------------------------------//

	public void saleApartment(Apartment apartment) throws Exception {
		try {
			Date curentDate = new Date();
			em.getTransaction().begin();
			apartment.setContractDate(curentDate);
			apartment.setReserveStatus(Lookup.APARTMENT_RESERVATIONSTATUS_RESERVED.getRecid());
			em.merge(apartment);
			// add first payment
			Payment firstpayment = new Payment();
			firstpayment.setCustomerId(apartment.getCustomerId());
			firstpayment.setMoney(apartment.getFirstPayment());
			firstpayment.setIsFirstPayment(1L);
			firstpayment.setApartmentId(apartment.getRecid());
			firstpayment.setPaymentDate(curentDate);
			firstpayment.setStatus(Lookup.PAYMENT_STATUS_FINISHED);
			em.persist(firstpayment);
			// add payments equivalence to No Of Payments
			List<Date> dl = Utils.getDateList(curentDate, apartment.getNoOfPayments().intValue());
			Double paymentValue = (apartment.getSalePrice() - apartment.getFirstPayment()) / apartment.getNoOfPayments();
			for (Date date : dl) {
				Payment payment = new Payment();
				payment.setCustomerId(apartment.getCustomerId());
				payment.setApartmentId(apartment.getRecid());
				payment.setIsFirstPayment(0L);
				payment.setPaymentDate(date);
				payment.setMoney(paymentValue);
				payment.setStatus(Lookup.PAYMENT_STATUS_NOTFINISHED);
				em.persist(payment);
			}
			Double totalRevenues = apartment.getFloor().getBuilding().getRevenues() + apartment.getFirstPayment();
			Building building = apartment.getFloor().getBuilding();
			building.setRevenues(totalRevenues);
			em.merge(building);
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}

	}

	public List<Customer> getCustomerListByName(String customerName) {
		return (List<Customer>) em.createQuery("select c from Customer c where c.name like :customerName").setParameter("customerName", "%" + customerName + "%").getResultList();

	}

	public void saveCustomer(Customer customer) throws EntityExistsException, Exception {
		try {
			em.getTransaction().begin();
			if (customer.getRecid() == null) {
				em.persist(customer);
			} else {
				em.merge(customer);
			}
			em.getTransaction().commit();
		} catch (EntityExistsException ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}
	}

	// -----------------------------------------------------------------------------------------//
	public List<PaymentResultSet> getPaymentResultSetList(PaymentResultSet paymentResultSet) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<PaymentResultSet> criteriaQuery = criteriaBuilder.createQuery(PaymentResultSet.class);
		Root<Payment> paymentRoot = criteriaQuery.from(Payment.class);

		Join<Apartment, Payment> apartmentPartmentRoot = paymentRoot.join("apartment", JoinType.LEFT);
		Join<Floor, Apartment> floorRoot = apartmentPartmentRoot.join("floor", JoinType.LEFT);
		Join<Building, Floor> buildingRoot = floorRoot.join("building", JoinType.LEFT);

		Join<Payment, Lookup> paymentLookupRoot = paymentRoot.join("status", JoinType.LEFT);
		Join<Payment, Customer> paymentCustomerRoot = paymentRoot.join("customer", JoinType.LEFT);

		criteriaQuery.select(criteriaBuilder.construct(PaymentResultSet.class, paymentRoot.get("recid"), paymentRoot.get("money"), paymentRoot.get("paymentDate"), paymentLookupRoot.get("recid"), paymentLookupRoot.get("name"), paymentRoot.get("customerId"), paymentCustomerRoot.get("name"), paymentRoot.get("apartmentId"), paymentRoot.get("isFirstPayment"), floorRoot.get("recid"), buildingRoot.get("recid"))).distinct(true);

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isNotEmpty(paymentResultSet)) {
			if (Utils.isNotEmpty(paymentResultSet.getCustomerId())) {
				criteriaTemp = criteriaBuilder.equal(paymentRoot.get("customerId"), paymentResultSet.getCustomerId());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (Utils.isNotEmpty(paymentResultSet.getDateFrom()) && Utils.isNotEmpty(paymentResultSet.getDateTo())) {
				Expression<Date> date1 = paymentRoot.get("paymentDate");
				criteriaTemp = criteriaBuilder.between(date1, paymentResultSet.getDateFrom(), paymentResultSet.getDateTo());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (Utils.isNotEmpty(paymentResultSet.getPaymentStatusId())) {
				criteriaTemp = criteriaBuilder.equal(paymentRoot.get("status").get("recid"), paymentResultSet.getPaymentStatusId());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			criteriaTemp = criteriaBuilder.notEqual(paymentRoot.get("isFirstPayment"), 1L);
			criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));

			if (null != criteria) {
				criteriaQuery.where(criteria);
			}
		}

		Query query = em.createQuery(criteriaQuery);
		return query.getResultList();
	}

	// public List<Payment> getPaymentList(Payment payment) {
	// CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
	// CriteriaQuery<Payment> criteriaQuery =
	// criteriaBuilder.createQuery(Payment.class);
	// Root<Payment> paymentRoot = criteriaQuery.from(Payment.class);
	// criteriaQuery.select(criteriaBuilder.construct(Payment.class,
	// paymentRoot.get("recid"), paymentRoot.get("money"),
	// paymentRoot.get("paymentDate"), paymentRoot.get("status"),
	// paymentRoot.get("customerId"),
	// paymentRoot.get("apartmentId"))).distinct(true);
	// // Join<Payment, Lookup> paymentLookupRoot =
	// // paymentRoot.join("status",JoinType.LEFT);
	// // Join<Payment, Customer> paymentCustomerRoot
	// // =paymentRoot.join("customer", JoinType.LEFT);
	// Predicate criteria = null;
	// Predicate criteriaTemp = null;
	// if (Utils.isNotEmpty(payment)) {
	// if (Utils.isNotEmpty(payment.getCustomerId())) {
	// criteriaTemp = criteriaBuilder.equal(paymentRoot.get("customerId"),
	// payment.getCustomerId());
	// criteria = (criteria == null ? criteriaTemp :
	// criteriaBuilder.and(criteria, criteriaTemp));
	// }
	// if (Utils.isNotEmpty(payment.getDateFrom()) &&
	// Utils.isNotEmpty(payment.getDateTo())) {
	// Expression<Date> date1 = paymentRoot.get("paymentDate");
	// criteriaTemp = criteriaBuilder.between(date1, payment.getDateFrom(),
	// payment.getDateTo());
	// criteria = (criteria == null ? criteriaTemp :
	// criteriaBuilder.and(criteria, criteriaTemp));
	// }
	// if (Utils.isNotEmpty(payment.getStatus()) &&
	// Utils.isNotEmpty(payment.getStatus().getRecid())) {
	// criteriaTemp =
	// criteriaBuilder.equal(paymentRoot.get("status").get("recid"),
	// payment.getStatus().getRecid());
	// criteria = (criteria == null ? criteriaTemp :
	// criteriaBuilder.and(criteria, criteriaTemp));
	// }
	// criteriaTemp =
	// criteriaBuilder.notEqual(paymentRoot.get("isFirstPayment"), 1L);
	// criteria = (criteria == null ? criteriaTemp :
	// criteriaBuilder.and(criteria, criteriaTemp));
	// if (null != criteria) {
	// criteriaQuery.where(criteria);
	// }
	// }
	// Query query = em.createQuery(criteriaQuery);
	// return query.getResultList();
	// }

	public Long getPaymentListRowCount(Payment payment) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
		Root<Payment> paymentRoot = criteriaQuery.from(Payment.class);
		criteriaQuery.select(criteriaBuilder.count(paymentRoot.get("recid")));
		Predicate criteria = null;
		Predicate criteriaTemp = null;
		if (Utils.isNotEmpty(payment)) {
			if (Utils.isNotEmpty(payment.getCustomer().getRecid())) {
				criteriaTemp = criteriaBuilder.equal(paymentRoot.get("customer").get("recid"), "%" + payment.getCustomerId() + "%");
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}
			if (null != criteria) {
				criteriaQuery.where(criteria);
			}
		}
		Query query = em.createQuery(criteriaQuery);
		return Long.parseLong(query.getSingleResult() + "");
	}

	public void payApayment(Payment payment) throws Exception {
		try {
			em.getTransaction().begin();
			payment.setStatus(Lookup.PAYMENT_STATUS_FINISHED);
			// ---------------//
			Building building = this.getBuildingById(payment.getApartment().getFloor().getBuilding().getRecid());
			Double totalRevenues = building.getRevenues() + payment.getMoney();
			building.setRevenues(totalRevenues);
			payment.setApartment(null);
			em.merge(payment);
			em.merge(building);
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		}
		// finally {
		// if (em != null) {
		// em.close();
		// }
		// }
	}

	// -------------------------------------------------------------------------//
	public List<Expens> getExpensesList(Long biulgingId) throws Exception {
		return (List<Expens>) em.createQuery("select e from Expens e where e.building.recid =:biulgingId").setParameter("biulgingId", biulgingId).getResultList();
	}

	public void saveExpenses(Expens expens) throws Exception {
		try {
			em.getTransaction().begin();
			if (Utils.isEmpty(expens.getRecid())) {
				expens.setDate(new Date());
				em.persist(expens);
			} else {
				em.merge(expens);
			}
			Building building = this.getBuildingById(expens.getBuildingId());
			Double totalExpenses = building.getExpenses() + expens.getMoney();
			building.setExpenses(totalExpenses);
			em.merge(building);
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			ex.printStackTrace();
			throw ex;
		}
	}

	public Long getExpensListRowCount(Expens expens) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
		Root<Expens> expensRoot = criteriaQuery.from(Expens.class);
		// criteriaQuery.select(expensRoot);
		criteriaQuery.select(criteriaBuilder.count(expensRoot.get("recid")));
		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isNotEmpty(expens)) {
			if (Utils.isNotEmpty(expens.getBuilding()) && Utils.isNotEmpty(expens.getBuilding().getRecid())) {
				criteriaTemp = criteriaBuilder.equal(expensRoot.get("buildingId"), expens.getBuildingId());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

			if (Utils.isNotEmpty(expens.getDateFrom()) && Utils.isNotEmpty(expens.getDateTo())) {
				Expression<Date> date1 = expensRoot.get("date");
				criteriaTemp = criteriaBuilder.between(date1, expens.getDateFrom(), expens.getDateTo());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

			if (null != criteria) {
				criteriaQuery.where(criteria);
			}
		}
		Query query = em.createQuery(criteriaQuery);
		return Long.parseLong(query.getSingleResult() + "");
	}

	public List<Expens> getExpensList(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters, Expens expens) {

		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Expens> criteriaQuery = criteriaBuilder.createQuery(Expens.class);
		Root<Expens> expensRoot = criteriaQuery.from(Expens.class);
		criteriaQuery.select(criteriaBuilder.construct(Expens.class, expensRoot.get("recid"), expensRoot.get("buildingId"), expensRoot.get("building").get("name"), expensRoot.get("date"), expensRoot.get("description"), expensRoot.get("money"))).distinct(true);

		Predicate criteria = null;
		Predicate criteriaTemp = null;

		if (Utils.isNotEmpty(expens)) {
			if (Utils.isNotEmpty(expens.getBuildingId())) {
				criteriaTemp = criteriaBuilder.equal(expensRoot.get("buildingId"), expens.getBuildingId());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

			if (Utils.isNotEmpty(expens.getDateFrom()) || Utils.isNotEmpty(expens.getDateTo())) {
				Expression<Date> date1 = expensRoot.get("date");
				criteriaTemp = criteriaBuilder.between(date1, expens.getDateFrom() == null ? Utils.getMinDate() : expens.getDateFrom(), expens.getDateTo() == null ? Utils.getMaxDate() : expens.getDateTo());
				criteria = (criteria == null ? criteriaTemp : criteriaBuilder.and(criteria, criteriaTemp));
			}

			if (null != criteria) {
				criteriaQuery.where(criteria);
			}
		}
		Query query = em.createQuery(criteriaQuery);
		if (first != -1 && pageSize != -1) {
			query.setMaxResults(pageSize);
			query.setFirstResult(first);
		}
		return query.getResultList();
	}

}
